// GCC header includes to get the parse tree declarations. The order
// is important and doesn't follow any kind of logic.
//

#include <stdlib.h>
#include <gmp.h>

#include <cstdlib> // Include before GCC poison some declarations.

extern "C"
{
#include "gcc-plugin.h"

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tree.h"
#include "tree-iterator.h"
#include "intl.h"

#include "tm.h"

#include "diagnostic.h"
#include "c-common.h"
#include "c-pragma.h"
#include "cp/cp-tree.h"
}

#include <set>
#include <string>
#include <iostream>

using namespace std;

int plugin_is_GPL_compatible;

inline const char *
_get_decl_name(tree decl)
{
    tree id (DECL_NAME (decl));
    const char* name (id
                      ? IDENTIFIER_POINTER (id)
                      : "<unnamed>");
    return name;
}

struct decl_comparator
{
    bool
    operator() (tree x, tree y) const
    {
        location_t xl (DECL_SOURCE_LOCATION (x));
        location_t yl (DECL_SOURCE_LOCATION (y));

        return xl < yl;
    }
};

typedef std::multiset<tree, decl_comparator> decl_set;

void
collect (tree ns, decl_set& set)
{
    tree decl;
    cp_binding_level* level (NAMESPACE_LEVEL (ns));

    // Collect declarations.
    //
    for (decl = level->names; decl != 0; decl = TREE_CHAIN (decl))
    {
        if (DECL_IS_BUILTIN (decl))
            continue;

        set.insert (decl);
    }

    // Traverse namespaces.
    //
    for(decl = level->namespaces; decl != 0; decl = TREE_CHAIN (decl))
    {
        // 加上这个，会过滤掉 std 命名空间等。。。
        // if (DECL_IS_BUILTIN (decl))
        //     continue;

        const char *nname = _get_decl_name(decl);

        fprintf(stderr, " >> namespace %s\n", nname);
        collect (decl, set);
    }
}

string
decl_scope (tree decl)
{
    string s, tmp;

    for (tree scope (CP_DECL_CONTEXT (decl));
         scope != global_namespace;
         scope = CP_DECL_CONTEXT (scope))
    {
        if (TREE_CODE (scope) == RECORD_TYPE)
            scope = TYPE_NAME (scope);

        tree id (DECL_NAME (scope));

        tmp = "::";
        tmp += (id != 0 ? IDENTIFIER_POINTER (id) : "<unnamed>");
        tmp += s;
        s.swap (tmp);
    }

    return s;
}

void
print_decl (tree decl);

enum access_spec
{
    public_, protected_, private_
};

const char* access_spec_str[] =
{
    "public", "protected", "private"
};

void
print_class (tree type)
{
    type = TYPE_MAIN_VARIANT (type);

    tree decl (TYPE_NAME (type));
    tree id (DECL_NAME (decl));
    const char* name (IDENTIFIER_POINTER (id));

    cerr << "class " << decl_scope (decl) << "::" << name
         << " at " << DECL_SOURCE_FILE (decl)
         << ":" << DECL_SOURCE_LINE (decl) << endl;

    // We are done if this is an incomplete
    // class declaration.
    //
    if (!COMPLETE_TYPE_P (type))
        return;

    // Traverse base information.
    //
    tree biv (TYPE_BINFO (type));
    size_t n (biv ? BINFO_N_BASE_BINFOS (biv) : 0);

    for (size_t i (0); i < n; i++)
    {
        tree bi (BINFO_BASE_BINFO (biv, i));

        // Get access specifier.
        //
        access_spec a (public_);

        if (BINFO_BASE_ACCESSES (biv))
        {
            tree ac (BINFO_BASE_ACCESS (biv, i));

            if (ac == 0 || ac == access_public_node)
                a = public_;
            else if (ac == access_protected_node)
                a = protected_;
            else
                a = private_;
        }

        bool virt (BINFO_VIRTUAL_P (bi));
        tree b_type (TYPE_MAIN_VARIANT (BINFO_TYPE (bi)));
        tree b_decl (TYPE_NAME (b_type));
        tree b_id (DECL_NAME (b_decl));
        const char* b_name (IDENTIFIER_POINTER (b_id));

        cerr << "\t" << access_spec_str[a] << (virt ? " virtual" : "")
             << " base " << decl_scope (b_decl) << "::" << b_name << endl;
    }

    // Traverse members.
    //
    decl_set set;

    for (tree d (TYPE_FIELDS (type)); d != 0; d = TREE_CHAIN (d))
    {
        switch (TREE_CODE (d))
        {
        case TYPE_DECL:
        {
            if (!DECL_SELF_REFERENCE_P (d))
                set.insert (d);
            break;
        }
        case FIELD_DECL:
        {
            if (!DECL_ARTIFICIAL (d))
                set.insert (d);
            break;
        }
        default:
        {
            set.insert (d);
            break;
        }
        }
    }

    for (tree d (TYPE_METHODS (type)); d != 0; d = TREE_CHAIN (d))
    {
        if (!DECL_ARTIFICIAL (d))
            set.insert (d);
    }

    for (decl_set::iterator i (set.begin ()), e (set.end ());
         i != e; ++i)
    {
        print_decl (*i);
    }
}

inline int
_get_real_lineno(int pos)
{
    const struct line_map *map
        = linemap_lookup (line_table, pos);

    if (map == NULL)
        return -1;

    return SOURCE_LINE (map, pos);
}

inline void
_print_ident(int depth)
{
    while (depth-- > 0)
        fprintf(stderr, "  ");
}

static void
_walk_block(tree block, int depth)
{
    while (block) {
        _print_ident(depth);
        fprintf(stderr, "%s, there is vars: %s\n",
                tree_code_name[TREE_CODE(block)],
                BLOCK_VARS(block) ? "yes" : "no");

        {
            tree vars = BLOCK_VARS(block);
            while (vars) {
                _print_ident(depth + 1);
                if (TREE_CODE(vars) == IMPORTED_DECL) {
                    tree imp = IMPORTED_DECL_ASSOCIATED_DECL(vars);
                    if (imp) {
                        fprintf(stderr, "name %s, at line %d, [%s]\n",
                                _get_decl_name(imp),
                                DECL_SOURCE_LINE(imp),
                                tree_code_name[TREE_CODE(imp)]);
                    }
                }
                else {
                    fprintf(stderr, "name %s, type %s, at line %d, [%s]\n",
                            _get_decl_name(vars),
                            tree_code_name[TREE_CODE(TREE_TYPE(vars))],
                            DECL_SOURCE_LINE(vars),
                            tree_code_name[TREE_CODE(vars)]);
                }
                vars = TREE_CHAIN(vars);
            }
        }

        _walk_block(BLOCK_SUBBLOCKS(block), depth + 1);

        block = BLOCK_CHAIN(block);
    }
}

static void
_print_tree_chain(tree t)
{
    while (t) {
        fprintf(stderr, "%s\n", tree_code_name[TREE_CODE(t)]);
        t = TREE_CHAIN(t);
    }
}

void
print_decl (tree decl)
{
    // TREE_TYPE 返回值的意义
    /* In all nodes that are expressions, this is the data type of the expression.
       In POINTER_TYPE nodes, this is the type that the pointer points to.
       In ARRAY_TYPE nodes, this is the type of the elements.
       In VECTOR_TYPE nodes, this is the type of the elements.  */

    // TYPE_ARG_TYPES 的参数一定要是 FUNCTION_TYPE
    // 返回 tree_list
    // 如果最后一个是 void_type 表示这个函数不是变长参数

    tree type (TREE_TYPE (decl));
    int dc (TREE_CODE (decl));
    int tc;

    fprintf(stderr, "  << %s: type %s, file %s, line %d >>\n",
            _get_decl_name(decl),
            tree_code_name[dc],
            DECL_SOURCE_FILE(decl),
            DECL_SOURCE_LINE(decl));

    if (type)
    {
        tc = TREE_CODE (type);

        if (dc == TYPE_DECL && tc == RECORD_TYPE)
        {
            // If DECL_ARTIFICIAL is true this is a class
            // declaration. Otherwise this is a typedef.
            //
            if (DECL_ARTIFICIAL (decl))
            {
                print_class (type);
                return;
            }
        }
    }

    if (dc == FUNCTION_DECL) {
        // tree args = DECL_ARGUMENTS(decl);

        // if (args) {
        //     while (args) {
        //         tree arg_type = DECL_ARG_TYPE(args);
        //         fprintf(stderr, "argument type is %s\n", tree_code_name[TREE_CODE(arg_type)]);

        //         // the argument is a pointer
        //         if (POINTER_TYPE_P(arg_type)) {
        //             do {
        //                 fprintf(stderr, " -> %s\n", tree_code_name[TREE_CODE(arg_type)]);

        //                 arg_type = TREE_TYPE(arg_type);
        //             } while (POINTER_TYPE_P(arg_type));

        //             fprintf(stderr, " -> real type is %s\n", tree_code_name[TREE_CODE(arg_type)]);
        //         }

        //         args = TREE_CHAIN(args);
        //     }
        // }
        // else {
        //     fprintf(stderr, "no, empty arguments\n");
        // }

        tree arguments = TYPE_ARG_TYPES(type);
        fprintf(stderr, "arguments:\n");
        while (arguments) {
            tree v = TREE_VALUE(arguments);

            fprintf(stderr, " -> %s\n", tree_code_name[TREE_CODE(v)]);

            arguments = TREE_CHAIN(arguments);
        }

        // 打印返回类型
        // DECL_RESULT 在函数声明，但没有定义的时候，会返回 NULL_TREE
        // 要用以下方法得到返回的类弄
        // 一些老代码是没有返回类型的，这种老函数默认是返回 int
        if (TREE_TYPE(decl))
            fprintf(stderr, "function return type: %s\n", tree_code_name[TREE_CODE(TREE_TYPE(TREE_TYPE(decl)))]);
        else
            fprintf(stderr, "default <int> function return type\n");

        if (DECL_INITIAL(decl)) {
            // 函数的定义行号区间
            struct function *f = DECL_STRUCT_FUNCTION(decl);

            if (f) {
                // function_start_locus 的位置可能是未知的
                // 这个时候用定义的位置作为行的开始
                fprintf(stderr, "function decl between %d - %d\n",
                        (f->function_start_locus == UNKNOWN_LOCATION)
                        ? DECL_SOURCE_LINE(decl)
                        : _get_real_lineno(f->function_start_locus),
                        _get_real_lineno(f->function_end_locus));

                tree func_decl = f->decl;
                if (func_decl) {
                    fprintf(stderr, "f->decl: %s\n", tree_code_name[TREE_CODE(func_decl)]);

                    if (DECL_SAVED_TREE(func_decl))
                        fprintf(stderr, "yes\n");
                    else
                        fprintf(stderr, "no\n");
                }
            }

            tree func_body = DECL_INITIAL(decl);
            // func_body 有可能是 error_mark 结点
            if (TREE_CODE(func_body) == BLOCK)
                _walk_block(func_body, 0);
        }
        else {
            fprintf(stderr, "it's function declare\n");
        }

        if (DECL_SAVED_TREE(decl))
            fprintf(stderr, "yes\n");
        else
            fprintf(stderr, "no\n");
    }

    if (dc == TEMPLATE_DECL) {
        tree ti = TI_TEMPLATE(decl);
        int  tc = TREE_CODE(ti);
        if (tc == RECORD_TYPE) {
            fprintf(stderr, "it's template class\n");

            fprintf(stderr, "template parms:\n");
            tree tparms = (DECL_TEMPLATE_PARMS(decl));
            while (tparms) {
                tree tv = TREE_VALUE(tparms);

                if (TREE_CODE(tv) == TREE_VEC) {
                    int len = TREE_VEC_LENGTH(tv);
                    fprintf(stderr, "length %d\n", len);
                    int i;
                    for (i = 0; i < len; i++) {
                        tree elt = TREE_VEC_ELT(tv, i);
                        fprintf(stderr, " -> %s, %s, %s\n",
                                tree_code_name[TREE_CODE(elt)],
                                tree_code_name[TREE_CODE(TREE_VALUE(elt))],
                                _get_decl_name(TREE_VALUE(elt)));
                    }
                }

                tparms = TREE_CHAIN(tparms);
            }

            print_class (ti);
        }
        else if (tc == FUNCTION_TYPE || tc == METHOD_TYPE) {
            fprintf(stderr, "it's template function: is method %s\n",
                    (tc == METHOD_TYPE)
                    ? "yes"
                    : "no");
            fprintf(stderr, "return type: %s\n", tree_code_name[TREE_CODE(TREE_TYPE(TI_TEMPLATE(decl)))]);
            fprintf(stderr, "arguments:\n");
            tree arguments = TYPE_ARG_TYPES(ti);
            while (arguments) {
                tree v = TREE_VALUE(arguments);
                if (v) {
                    fprintf(stderr, " -> %s\n", tree_code_name[TREE_CODE(v)]);
                    if (TREE_CODE(v) == TEMPLATE_TYPE_PARM) {
                        tree parm_decl = TEMPLATE_TYPE_DECL(v);
                        if (parm_decl) {
                            fprintf(stderr, "   -> with type %s\n", _get_decl_name(parm_decl));
                        }
                    }
                }
                arguments = TREE_CHAIN(arguments);
            }
            fprintf(stderr, "template parms:\n");
            tree tparms = (DECL_TEMPLATE_PARMS(decl));
            while (tparms) {
                tree tv = TREE_VALUE(tparms);

                if (TREE_CODE(tv) == TREE_VEC) {
                    int len = TREE_VEC_LENGTH(tv);
                    fprintf(stderr, "length %d\n", len);
                    int i;
                    for (i = 0; i < len; i++) {
                        tree elt = TREE_VEC_ELT(tv, i);
                        fprintf(stderr, " -> %s, %s, %s\n",
                                tree_code_name[TREE_CODE(elt)],
                                tree_code_name[TREE_CODE(TREE_VALUE(elt))],
                                _get_decl_name(TREE_VALUE(elt)));
                    }
                }

                tparms = TREE_CHAIN(tparms);
            }
        }
        else {
            fprintf(stderr, "unknown template type: %s\n", tree_code_name[tc]);
        }
    }
}

void
traverse (tree ns)
{
    decl_set set;
    collect (ns, set);

    for (decl_set::iterator i (set.begin ()), e (set.end ());
         i != e; ++i)
    {
        print_decl (*i);
    }
}

// static int
// dump_macro (cpp_reader *pfile, cpp_hashnode *node, void *v ATTRIBUTE_UNUSED)
// {
//   if (node->type == NT_MACRO && !(node->flags & NODE_BUILTIN))
//   {
//       fputs ("#define ", stdout);
//       fputs ((const char *) cpp_macro_definition (pfile, node), stdout);
//       putc ('\n', stdout);
//   }

//   return 1;
// }

static void
_collect_func_call(tree body, int depth)
{
    if (!body)
        return;

    int code = TREE_CODE(body);

    const char *fn = NULL;
    int call_line = 0;

    if (code == CALL_EXPR) {
        call_line = EXPR_LINENO(body);
        tree tmp = CALL_EXPR_FN(body);
        if (tmp && TREE_CODE(tmp) == ADDR_EXPR) {
            tmp = TREE_OPERAND(tmp, 0);
            if (tmp && TREE_CODE (tmp) == FUNCTION_DECL) {
                fn = _get_decl_name(tmp);
            }
        }
    }

    _print_ident(depth);

    if (fn) {
        fprintf(stderr, "%s <call %s at %d>\n",
                tree_code_name[code], fn, call_line);
    }
    else {
        fprintf(stderr, "%s\n",
                tree_code_name[code]);
    }

    if (code == STATEMENT_LIST) {
        tree_stmt_iterator it = tsi_start(body);
        while (it.ptr) {
            tree stmt = it.ptr->stmt;

            if (TREE_CODE(stmt) == BIND_EXPR)
                _collect_func_call(BIND_EXPR_BODY(stmt), depth + 1);
            else
                _collect_func_call(stmt, depth + 1);

            tsi_next(&it);
        }
    }
    else {
        if (EXPR_P(body)) {
            int oplen = TREE_OPERAND_LENGTH(body);
            int i;
            for (i = 0; i < oplen; i++) {
                tree op = TREE_OPERAND(body, i);
                _collect_func_call(op, depth + 1);
            }
        }
    }
}

extern "C" void
pre_genericize (void *gcc_data, void *user_data)
{
    // If there were errors during compilation,
    // let GCC handle the exit.
    //
    if (errorcount || sorrycount)
        return;

    // cpp_forall_identifiers (parse_in, dump_macro, NULL);
    // cpp_macro_info *mi = my_cpp_macro_exp;

    // while (mi) {
    //     printf("%s:%d %s\n", mi->file_name, mi->line, mi->def);
    //     mi = mi->next;
    // }

    // traverse (global_namespace);
    struct function *f = cfun;

    tree decl = f->decl;

    fprintf(stderr, "  << %s: type %s, file %s, line %d >>\n",
            _get_decl_name(decl),
            tree_code_name[TREE_CODE(decl)],
            DECL_SOURCE_FILE(decl),
            DECL_SOURCE_LINE(decl));

    // 这是函数定义
    if (DECL_INITIAL(decl)) {
        fprintf(stderr, "     defined between %d - %d\n",
                (f->function_start_locus == UNKNOWN_LOCATION)
                ? DECL_SOURCE_LINE(decl)
                : _get_real_lineno(f->function_start_locus),
                _get_real_lineno(f->function_end_locus));

        // which function called in this function
        tree body = DECL_SAVED_TREE(decl);
        _collect_func_call(body, 0);
    }
}

extern "C" int
plugin_init (plugin_name_args* info,
             plugin_gcc_version*)
{
    int r (0);

    cerr << "starting " << info->base_name << endl;

    fprintf(stderr, "processing %s\n", main_input_filename);

    //
    // Parse options if any.
    //

    // Disable assembly output.
    //
    asm_file_name = HOST_BIT_BUCKET;

    // Register callbacks.
    //

    // 在每个方法 genericize 前调用
    // 这可以获得 c 和 c++ 的语言独立的 AST
    register_callback (info->base_name,
                       PLUGIN_PRE_GENERICIZE,
                       &pre_genericize,
                       0);

    return r;
}
